﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RefactorThis.GraphDiff.Attributes;
using TanmiaGrp.Basis.Core;

namespace TanmiaGrp.GeneralLedger.Core
{
    public class Journal : BusinessObject
    {
        #region Private Fields
        private int journalID;
        private string notes;
        private PostStateEnum postState;
        private BusinessObjectCollection<JournalEntry> entries;
        private double totalCredit;
        private double totalDebit;
        private double balance;

        #endregion

        #region Public Properties

        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int JournalID
        {
            get { return journalID; }
            set
            {
                if (journalID != value)
                {
                    journalID = value;
                    RaisePropertyChanged(() => JournalID);
                }
            }
        }

        [Required]
        public PostStateEnum PostState
        {
            get { return postState; }
            set
            {
                if (postState != value)
                {
                    postState = value;
                    RaisePropertyChanged(() => PostState);
                }
            }
        }

        [MaxLength(255)]
        public string Notes
        {
            get { return notes; }
            set
            {
                if (notes != value)
                {
                    notes = value;
                    RaisePropertyChanged(() => Notes);
                }
            }
        }

        public double TotalDebit
        {
            get { return totalDebit; }
            set
            {
                if (totalDebit != value)
                {
                    totalDebit = value;
                    RaisePropertyChanged(() => TotalDebit);
                }
            }
        }

        public double TotalCredit
        {
            get { return totalCredit; }
            set
            {
                if (totalCredit != value)
                {
                    totalCredit = value;
                    RaisePropertyChanged(() => TotalCredit);
                }
            }
        }

        [Display(Name = "الرصيد", ShortName = "الرصيد")]
        [DisplayName("الرصيد")]
        public double Balance
        {
            get { return balance; }
            set
            {
                if (balance != value)
                {
                    balance = value;
                    RaisePropertyChanged(() => Balance);
                }
            }
        }

        [AssociationType(AssociationTypeEnum.Composition)]
        [Owned]
        public BusinessObjectCollection<JournalEntry> Entries
        {
            get { return entries; }
            set
            {
                if (entries != value)
                {
                    entries = value;
                    RaisePropertyChanged(() => Entries);

                    ((BusinessObjectCollection<JournalEntry>)Entries).CollectionChanged += OnEntriesChanged;
                }
            }
        }

        #endregion Public Properties

        public Journal()
        {
            CreateDate = DateTime.Now;
        }

        private void OnEntriesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                INotifyPropertyChanged bizObj = e.NewItems[0] as INotifyPropertyChanged;
                if (bizObj != null)
                    bizObj.PropertyChanged += OnEntryPropertyChanged;
            }
        }

        private void OnEntryPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            TotalCredit = Entries.Sum(x => x.Credit);
            TotalDebit = Entries.Sum(x => x.Debit);
            Balance = TotalDebit - TotalCredit;

            Validate();
        }

        #region Overridden Members

        public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (validationContext.ObjectInstance == this)
            {
                if (Balance != 0)
                    yield return new ValidationResult("يجب أن يكون قيد اليومية متوازن.", new string[] { "Balance" });
            }
        }
        #endregion

        #region Mapping

        public Voucher ToPostVoucher()
        {
            if (JournalID <= 0)
                throw new Exception("يجب حفظ اليومية قبل تحويلها إلى قيد.");

            Voucher result = new Voucher
            {
                CreateDate = DateTime.Now,
                Balance = this.Balance,
                Source = "Journal",
                SourceID = JournalID.ToString(),
                TotalCredit = TotalCredit,
                TotalDebit = TotalDebit,
                Notes = "ترحيل يومية"
            };

            foreach (JournalEntry entry in Entries)
                result.VoucherDetail.Add(entry.ToPostVoucherDetail());

            return result;
        }

        public Voucher ToUnpostVoucher()
        {
            if (JournalID <= 0)
                throw new Exception("يجب حفظ اليومية قبل تحويلها إلى قيد.");

            Voucher result = new Voucher
            {
                CreateDate = DateTime.Now,
                Balance = this.Balance,
                Source = "Journal",
                SourceID = JournalID.ToString(),
                TotalCredit = TotalDebit,
                TotalDebit = TotalCredit,
                Notes = "فك ترحيل يومية"
            };

            foreach (JournalEntry entry in Entries)
                result.VoucherDetail.Add(entry.ToUnpostVoucherDetail());

            return result;
        }
        #endregion
    }
}